跳到主要内容

Socket 编程

Linux 中建立 TCP 连接的过程

在 Linux 中,TCP连接的建立使用了经典的三次握手(Three-Way Handshake)过程。在TCP协议栈中,涉及到多个方法和函数来实现连接的建立。以下是一些相关方法和函数的概述:

  1. socket():该方法用于创建一个套接字(socket),并返回套接字文件描述符。套接字可以是流式套接字(SOCK_STREAM)或数据报套接字(SOCK_DGRAM)。

  2. bind():该方法用于将套接字与特定的IP地址和端口号进行绑定。这是在服务器端监听特定IP地址和端口号的必要步骤。

  3. listen():该方法用于将套接字设置为监听模式,使其可以接受连接请求。

  4. accept():该方法用于接受客户端的连接请求,并创建一个新的套接字来处理与客户端的通信。

  5. connect():该方法用于客户端发起连接请求,向服务器发送连接请求,并建立与服务器的连接。

  6. send() 和 recv():这些方法用于在建立连接后,进行数据的发送和接收。send()用于发送数据,recv()用于接收数据。

  7. close():该方法用于关闭套接字连接,释放相关资源。

以上是连接建立的基本方法和函数,其中 socket()、bind()、listen() 和 accept() 主要用于服务器端,而 connect() 则用于客户端。在Linux中,这些方法和函数是TCP连接建立过程中的关键组成部分。

整个过程如下图所示

Golang 中建立 TCP 连接的过程

服务器端的实现

package main

import (
"fmt"
"net"
"os"
)

func main() {
// 定义服务器监听地址和端口
listenAddr := "127.0.0.1:8080"

// 创建监听器
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
fmt.Println("Error listening:", err)
os.Exit(1)
}
defer listener.Close()

fmt.Println("Server listening on", listenAddr)

// 接受客户端连接
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
os.Exit(1)
}

fmt.Println("Accepted connection from", conn.RemoteAddr().String())

// 处理客户端请求
go handleClient(conn)
}
}

// 处理客户端连接
func handleClient(conn net.Conn) {
defer conn.Close()

// 读取客户端发送的数据
buffer := make([]byte, 1024)
bytesRead, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error reading data:", err)
return
}

request := string(buffer[:bytesRead])
fmt.Println("Received data from client:", request)

// 发送响应给客户端
response := "Hello, Client!"
_, err = conn.Write([]byte(response))
if err != nil {
fmt.Println("Error sending data:", err)
return
}

fmt.Println("Sent response to client:", response)
}

在此示例中,我们创建了一个TCP服务器,通过 net.Listen 函数监听指定的地址和端口。然后,我们在一个无限循环中接受客户端的连接。对于每个接受的连接,我们使用 go 关键字在新的 goroutine 中调用 handleClient 函数来处理客户端的请求。

handleClient 函数中,我们首先读取客户端发送的数据,然后打印出接收到的请求。接着,我们发送一个简单的响应给客户端。最后关闭与客户端的连接。

客户端的实现

package main

import (
"fmt"
"net"
"os"
)

func main() {
// 定义服务器地址和端口
serverAddr := "127.0.0.1:8080"

// 创建TCP连接
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
fmt.Println("Error connecting to server:", err)
os.Exit(1)
}
defer conn.Close()

fmt.Println("Connected to server:", serverAddr)

// 在连接上发送数据
message := "Hello, Server!"
_, err = conn.Write([]byte(message))
if err != nil {
fmt.Println("Error sending data:", err)
return
}

fmt.Println("Sent data to server:", message)

// 接收来自服务器的响应
buffer := make([]byte, 1024)
bytesRead, err := conn.Read(buffer)
if err != nil {
fmt.Println("Error receiving data:", err)
return
}

response := string(buffer[:bytesRead])
fmt.Println("Received response from server:", response)
}

References

socket编程到底是什么? - 小林coding的回答 - 知乎